/******************************
 * 请求 api （基于fetch）
******************************/
// type or interface
type InterceptorsRequest = <T = RequestOption>(config: T) => T
type InterceptorsResponse = <T = RequestOption>(config: T) => T
type InterceptorsError = (result: any) => any

type InterceptorsReq = {
	success: InterceptorsRequest,
	error: InterceptorsError
}[]
type InterceptorsRes = {
	success: InterceptorsResponse,
	error: InterceptorsError
}[]
interface Interceptors {
	request: {
		use: (success: InterceptorsReq, error: InterceptorsError) => {
			success: InterceptorsReq;
			error: InterceptorsError;
		};
		eject: (T: any) => void;
		Stack: InterceptorsReq
	};
	response: {
		use: (success: InterceptorsRes, error: InterceptorsError) => {
			success: InterceptorsRes;
			error: InterceptorsError;
		};
		eject: (T: any) => void;
		Stack: InterceptorsRes
	};
}

type RequestMethod = (config?: RequestOptionMethod) => RequestMethodTask
type RequestMethod2 = (config?: RequestOptionMethod) => RequestMethodTask
type RequestMethodTask = (url: string, config: RequestOption) => RequestTask
type RequestMethodTask2 = (url: string, data: Record<string, any>, config: RequestOption) => RequestTask
type RequestCreate = (config: RequestOption) => Request
type RequestAll = (requestList: RequestTask[]) => RequestTask
export interface Request {
	(configOrUrl: RequestOption | string, config?: RequestOption): RequestTask

	get?: RequestMethodTask
	delete?: RequestMethodTask
	head?: RequestMethodTask
	post?: RequestMethodTask2
	put?: RequestMethodTask2
	all?: RequestAll;
	create?: RequestCreate;
	defaults?: RquestConfig;
	interceptors?: Interceptors;
}
interface RquestConfig {
	baseURL: string
	data: Record<string, any>
	dataType: 'json' | 'text'
	method: RequestOptionMethod
	headers: Record<string, any>
	timeout: number
}
type RequestOptionMethod =
	| 'OPTIONS'
	| 'GET'
	| 'HEAD'
	| 'POST'
	| 'PUT'
	| 'DELETE'
	| 'TRACE'
	| 'CONNECT'
interface RequestOption {
	/** `url` 是用于请求的服务器 URL */
	url?: string
	/** `method` 是创建请求时使用的方法 */
	baseURL?: string
	/** 接口调用结束的回调函数（调用成功、失败都会执行） */
	complete?: RequestCompleteCallback
	/** 请求的参数 */
	data?: Record<string, any> | string
	/** 返回的数据格式
	 *
	 * 可选值：
	 * - 'json': 返回的数据为 JSON，返回后会对返回的数据进行一次 JSON.parse;
	 * - '其他': 不对返回的内容进行 JSON.parse; */
	dataType?: 'json' | 'text'
	/** 接口调用失败的回调函数 */
	fail?: RequestFailCallback
	/** 设置请求的 header，header 中不能设置 Referer。
	 *
	 * `content-type` 默认为 `application/json` */
	headers?: Record<string, any>
	/** HTTP 请求方法
	 *
	 * 可选值：
	 * - 'OPTIONS': HTTP 请求 OPTIONS;
	 * - 'GET': HTTP 请求 GET;
	 * - 'HEAD': HTTP 请求 HEAD;
	 * - 'POST': HTTP 请求 POST;
	 * - 'PUT': HTTP 请求 PUT;
	 * - 'DELETE': HTTP 请求 DELETE;
	 * - 'TRACE': HTTP 请求 TRACE;
	 * - 'CONNECT': HTTP 请求 CONNECT; */
	method?: RequestOptionMethod
	/** 接口调用成功的回调函数 */
	success?: RequestSuccessCallback
	/** 超时时间，单位为毫秒
	 *
	 * 最低基础库： `2.10.0` */
	timeout?: number
	/** 
	 * 拦截器
	 */
	__interceptors__?: Interceptors
	__defaults__?: RquestConfig
}
interface RequestOptionInit extends RequestOption {
	data?: string
}
interface RequestTask extends Promise<any> {
	/** [RequestTask.abort()](https://developers.weixin.qq.com/miniprogram/dev/api/network/request/RequestTask.abort.html)
	 *
	 * 中断请求任务
	 *
	 * 最低基础库： `1.4.0` */
	abort?(): void
}
/** 接口调用结束的回调函数（调用成功、失败都会执行） */
type RequestCompleteCallback = () => void
/** 接口调用失败的回调函数 */
type RequestFailCallback = (res: GeneralCallbackResult) => void
/** 接口调用成功的回调函数 */
type RequestSuccessCallback = (result: RequestSuccessCallbackResult) => void
interface GeneralCallbackResult {
	errMsg: string
}
interface RequestSuccessCallbackResult {
	/** 开发者服务器返回的 cookies，格式为字符串数组
	 *
	 * 最低基础库： `2.10.0` */
	cookies: string[]
	/** 开发者服务器返回的数据 */
	data: string | Record<string, any> | ArrayBuffer
	/** 开发者服务器返回的 HTTP Response Header
	 *
	 * 最低基础库： `1.2.0` */
	header: Record<string, any>
	/** 开发者服务器返回的 HTTP 状态码 */
	statusCode: number
	errMsg: string
}


/******************************
 * JS代码部分
******************************/
const copyJson = <T = object>(json: T): T => {
	try {
		return JSON.parse(JSON.stringify(json))
	} catch {
		return json
	}
}
function assign(...args: object[]): object {
	return Object.assign({}, ...Array.from(args).map(obj => copyJson(obj)))
}

/**
 * 默认数据
 */
const defaults: RquestConfig = {
	baseURL: '',
	data: {},
	dataType: 'json',
	method: 'GET',
	headers: { 'content-type': 'application/json' },
	timeout: 60000
}

// 请求拦截方法存放
const req_interceptors: InterceptorsReq = []
// 请求拦截方法存放
const res_interceptors: InterceptorsRes = []

// 挂载添加和删除拦截器的方法
function interceptorsInit<T extends any[]>(Stack: T): {
	use: (success: T, error: InterceptorsError) =>
		{ success: T, error: InterceptorsError },
	eject: (T) => void,
	Stack: T
} {
	return {
		use(success, error) {
			let _list = { success, error }
			Stack.push(_list)
			return _list
		},
		eject(data) {
			data !== undefined
				? Stack.indexOf(data) !== -1 && Stack.splice(Stack.indexOf(data), 1)
				: ''
		},
		Stack
	}
}
// 挂载添加和删除拦截器的方法
const interceptors: Interceptors = {
	request: interceptorsInit<InterceptorsReq>(req_interceptors),
	response: interceptorsInit<InterceptorsRes>(res_interceptors),
}

/**
 * 将 request 的 options 进行设置
 * （config默认配置 和 拦截器 对options更新）
 */
function requestInit(
	configOrUrl: RequestOption | string,
	config: RequestOption = {},
): RequestOptionInit {
	typeof configOrUrl === 'string'
		? (config.url = configOrUrl)
		: (config = configOrUrl)
	config.__defaults__ = config.__defaults__ || defaults
	config.__interceptors__ = config.__interceptors__ || interceptors
	try {
		config.headers = config.headers || {}
		config.method = (config.method ? config.method.toUpperCase() : config.__defaults__?.method) as RequestOptionMethod
		config.data = assign(config.__defaults__?.data, config.data as object || {})
		config.dataType = config.dataType || config.__defaults__?.dataType
		config.timeout = config.timeout || config.__defaults__?.timeout
		let params = ''
		if (config.method === 'GET' || config.method === 'HEAD') {
			// fetch的GET/HEAD请求不能设置BODY
			Object.keys(config.data).forEach((key, index) => {
				params += (index === 0 ? '' : '&') + key + '=' + config.data[key]
			})
			config.data = null
		}

		// 无data，则content-type为application/x-www-form-urlencoded
		config.headers['content-type'] = config.data
			? (config.headers['content-type'] || 'application/json')
			: 'application/x-www-form-urlencoded'
		config.headers = assign(config.__defaults__?.headers, config.headers)
		config.url = config.__defaults__?.baseURL + config.url
			+ (params ? config.url.indexOf('?') === -1 ? '?' : '&' : '')
			+ params
		// 拦截器重新设置options
		config.__interceptors__?.request.Stack.forEach(({ success }) => config = success(config))
		config.data = config.data && JSON.stringify(config.data)
	} catch (err) {
		config.__interceptors__?.request.Stack.forEach(({ error }) => error?.(err))
	}

	return config as RequestOptionInit
}

/**
 * 请求封装（基于fetch）
 * @param config 请求配置
 * @return {Promise} 返回Promise对象
 */
let request: Request = (configOrUrl, config) => {
	let { url, complete, data, dataType, fail, headers, method, success, timeout }
		= config = requestInit(configOrUrl, config)

	const controller = new AbortController()
	let signal = controller.signal;
	class _Promise<T> extends Promise<T>{
		constructor(resolve) {
			super(resolve)
			this.abort = controller.abort.bind(controller)
		}
		abort: () => void
	}

	return new _Promise((resolve, reject) => {
		let timeOut = setTimeout(() => {
			controller.abort();
			return reject({
				code: 408,
				message: "Request Timeout：请求超时"
			});
		}, timeout);

		fetch(url, {
			body: data,
			headers,
			method,
			signal
		})
			.then(res => {
				if (res.status >= 200 && res.status < 300) {
					return dataType === 'json' ? res.json() : res.text()
				} else {
					Promise.reject(res)
				}
			})
			.then((result) => {
				let oldResult = result
				//多个响应拦截器依次更改res 
				config.__interceptors__?.response.Stack.forEach(({ success }) => result = success(result))
				if (result) {
					success?.(result)
					resolve(result)
				} else {
					// 拦截器返回false，则执行error错误
					Promise.reject(oldResult)
				}
			})
			.catch(err => {
				config.__interceptors__?.response.Stack.forEach(({ error }) => error?.(err))
				fail?.(err)
				reject(err)
			})
			.finally(() => {
				clearTimeout(timeOut)
				complete?.()
			})
	})
}

const requestMethod: RequestMethod = (method = 'GET') =>
	(url, config = {}) => request(assign(config, { url, method }))
const requestMethod2: RequestMethod2 = (method = 'POST') =>
	(url, data = {}, config = {}) => request(assign(config, { url, method, data }))

const requestAll: RequestAll = (requestList) => {
	let siganl = []
	class _Promise<T> extends Promise<T>{
		constructor(resolve) {
			super(resolve)
			this.abort = () => siganl.forEach(abort => abort())
		}
		abort: () => void
	}

	let aResult = [];    // 用于存放每次执行后返回结果
	return new _Promise(function (resolve, reject) {
		let i = 0;
		next();    // 开始逐次执行数组中的函数(重要)
		function next() {
			let task: RequestTask = requestList[i].then(res => {
				aResult.push(res);    // 存储每次得到的结果
				i++;
				i == requestList.length ? resolve(aResult) : next()
			}).catch(err => reject(err))
			siganl.push(task.abort)
		}
	})
}

const requestCreate: RequestCreate = (_c = {}) => {
	const req_i: InterceptorsReq = [...req_interceptors]
	const res_i: InterceptorsRes = [...res_interceptors]

	let _i: Interceptors = {
		request: interceptorsInit<InterceptorsReq>(req_i),
		response: interceptorsInit<InterceptorsRes>(res_i),
	}
	_c.headers && (_c.headers = assign(defaults.headers, _c.headers))
	let __defaults__ = assign(defaults, _c) as RquestConfig
	let _reqeust: Request = (configOrUrl, config = {}) => {
		typeof configOrUrl === 'string' && (config.url = configOrUrl)
		return request(Object.assign(assign(__defaults__, config, { __defaults__ }), { __interceptors__: _i }))
	}

	const _requestMethod: RequestMethod = (method = 'GET') =>
		(url, config = {}) => _reqeust(assign(config, { url, method }))
	const _requestMethod2: RequestMethod2 = (method = 'POST') =>
		(url, data = {}, config = {}) => _reqeust(assign(config, { url, method, data }))

	_reqeust.get = _requestMethod('GET')
	_reqeust.delete = _requestMethod('DELETE')
	_reqeust.head = _requestMethod('HEAD')
	_reqeust.post = _requestMethod2('POST')
	_reqeust.put = _requestMethod2('PUT')
	_reqeust.defaults = __defaults__
	_reqeust.interceptors = _i
	return _reqeust
}

request.get = requestMethod('GET');
request.delete = requestMethod('DELETE');
request.head = requestMethod('HEAD');
request.post = requestMethod2('POST');
request.put = requestMethod2('PUT');
request.all = requestAll;
request.create = requestCreate;
request.defaults = defaults;
request.interceptors = interceptors;

let api = { request }
window['$W'] = Object.assign(window['$W'] || {}, api)
export default api